home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 July / EnigmA AMIGA RUN 20 (1997)(G.R. Edizioni)(IT)[!][issue 1997-07 & 08][EAR-CD IV].iso / lightwave / arexx_macros / hair.lwm < prev    next >
Text File  |  1996-12-09  |  11KB  |  385 lines

  1. /* CMD: Random Surface Points
  2.  *      and Lines
  3.  * Generate a set of points randomly distributed across the surface of
  4.  * an object by pricking it with many tiny needles.
  5.  *
  6.  * Redone in 08/94 to create hair by Gonzalo Garramuo
  7.  * To create Hair:  cut all the polygons of your head that should have
  8.  *                  hair to a new layer and run this program.  To create
  9.  *                  realistic hair you should use pretty high numbers
  10.  *                  (2000-10000) in the Number of Points Requester.
  11.  *                  With this high numbers, it can take a while.
  12.  *                  After the macro finishes, you may still want to
  13.  *                  retouch the hair a little bit, to avoid it to go
  14.  *                  through the ears of your character, to "comb" it, etc.
  15.  *                  The "hair" are made of a huge number of lines (2-point
  16.  *                  particles or polygons), therefore, you should use
  17.  *                  shadows when rendering.  Why?  Because these 2-
  18.  *                  sided polygons will receive light a little different
  19.  *                  than normal polygons: they won't be shaded when they
  20.  *                  should be.
  21.  * version 2.1 10/94    by Gonzalo Garramuo
  22.  *                      added segmentation to lines (to allow "combing")
  23.  * version 2.2 10/94    by Gonzalo Garramuo
  24.  *                      added rotation center and sizing center
  25.  */
  26.  
  27. syscode = "Random Surface Points"
  28. statfil = 'T:RandomPoints.state'
  29. version = 'Prick v2.2'
  30.  
  31. /* Boilerplate.*/
  32.  
  33. mxx = "LWModelerARexx.port"
  34. mxx_add = addlib(mxx,0)
  35. signal on error
  36. signal on syntax
  37. call addlib("rexxmathlib.library",0,-30,0)
  38. lines = 0
  39. call main
  40. if (mxx_add) then call remlib(mxx)
  41. exit
  42.  
  43. syntax:
  44. error:
  45. t = Notify(1,'!Rexx Script Error','@'ErrorText(rc),'Line 'SIGL)
  46. if (mxx_add) then call remlib(mxx)
  47. exit
  48.  
  49. main:
  50.  
  51. fg = curlayer()
  52. emp = emptylayers()
  53. if (words(emp) < 2) then do
  54.     call notify(1,"!Need two background layers for scratch work.")
  55.     return
  56. end
  57. temp = word(emp,1)
  58. temp2 = word(emp,2)
  59.  
  60. /* Get bounding box for fg data. */
  61. parse value boundingbox() with n x1 x2 y1 y2 z1 z2 .
  62. if (n < 3) then do
  63.         call notify(1,"!I need at least a 3-sided polygon to work on.")
  64.         return
  65. end
  66.  
  67. center = (x1+x2)/2 (y1+y2)/2 (z1+z2)/2
  68.  
  69. if (setup() = 0) then return
  70.  
  71. lo = x1 y1 z1
  72. hi = x2 y2 z2
  73. dx = x2 - x1
  74. dy = y2 - y1
  75. dz = z2 - z1
  76.  
  77. /* Compute our slightly expanded box for the ends of the needles. */
  78. exb = max(dx, dy, dz) / 20
  79. xx1 = x1 - exb
  80. xx2 = x2 + exb
  81. yy1 = y1 - exb
  82. yy2 = y2 + exb
  83. zz1 = z1 - exb
  84. zz2 = z2 + exb
  85.  
  86. /* Compute distribution cell size.  We want about "num" points
  87.  * distributed along the surface of the bounding box, so take that
  88.  * surface area, divide by num.  Square root of that is the cell
  89.  * edge size (s).  Also compute min allowed distance (md) and
  90.  * tiny delta for needle construction (d).
  91.  */
  92. sa = 2 * (dx * dy + dy * dz + dz * dx)
  93. s = sqrt(sa / num)
  94. md = s / 4
  95. d = md / 20
  96. say "Cell edge size:" s "  Merge dist:" md
  97.  
  98. /* Goto background layer and build our lattice of needles. */
  99. call setlayer(temp)
  100. call add_begin()
  101.  
  102. nx = dx % s
  103. ny = dy % s
  104. nz = dz % s
  105. say "Needle grid" nx ny nz
  106. call meter_begin(nx * ny + nx * nz + ny * nz, syscode)
  107.  
  108. do xi = 1 to nx
  109.     x0 = x1 + (x2 - x1) / nx * (xi - 0.5)
  110.     do yi = 1 to ny
  111.         y0 = y1 + (y2 - y1) / ny * (yi - 0.5)
  112.         xa = x0 + (randu()-0.5) * s
  113.         xb = x0 + (randu()-0.5) * s
  114.         ya = y0 + (randu()-0.5) * s
  115.         yb = y0 + (randu()-0.5) * s
  116.         i1 = add_point(xa ya   zz1)
  117.         i2 = add_point(xa ya+d zz1)
  118.         i3 = add_point(xb yb   zz2)
  119.         call add_polygon(i1 i2 i3)
  120.         call meter_step()
  121.     end yi
  122. end xi
  123.  
  124. do xi = 1 to nx
  125.     x0 = x1 + (x2 - x1) / nx * (xi - 0.5)
  126.     do zi = 1 to nz
  127.         z0 = z1 + (z2 - z1) / nz * (zi - 0.5)
  128.         xa = x0 + (randu()-0.5) * s
  129.         xb = x0 + (randu()-0.5) * s
  130.         za = z0 + (randu()-0.5) * s
  131.         zb = z0 + (randu()-0.5) * s
  132.         i1 = add_point(xa yy1 za)
  133.         i2 = add_point(xa yy1 za+d)
  134.         i3 = add_point(xb yy2 zb)
  135.         call add_polygon(i1 i2 i3)
  136.         call meter_step()
  137.     end zi
  138. end xi
  139.  
  140. do yi = 1 to ny
  141.     y0 = y1 + (y2 - y1) / ny * (yi - 0.5)
  142.     do zi = 1 to nz
  143.         z0 = z1 + (z2 - z1) / nz * (zi - 0.5)
  144.         ya = y0 + (randu()-0.5) * s
  145.         yb = y0 + (randu()-0.5) * s
  146.         za = z0 + (randu()-0.5) * s
  147.         zb = z0 + (randu()-0.5) * s
  148.         i1 = add_point(xx1 ya za)
  149.         i2 = add_point(xx1 ya za+d)
  150.         i3 = add_point(xx2 yb zb)
  151.         call add_polygon(i1 i2 i3)
  152.         call meter_step()
  153.     end zi
  154. end yi
  155.  
  156. call meter_end()
  157. call add_end()
  158.  
  159. /* Perform the slice and leave only the points. */
  160. call setblayer(fg)
  161. call soliddrill(SLICE)
  162. call removepols()
  163.  
  164. /* Delete the points that lie outside the bounding box of the
  165.  * original object, leaving the ones that intersected the interior
  166.  * surfaces.  Then merge points using our min distance. */
  167. call sel_mode(USER)
  168. call sel_point(SET)
  169. d = exb / 2
  170. call sel_point(CLEAR, VOL, x1-d y1-d z1-d, x2+d y2+d z2+d)
  171. call cut()
  172. call mergepoints(md)
  173.  
  174. if (lines) then call hair
  175.  
  176. if same = 1 then call CUT()
  177. call SETLAYER(temp2)
  178.  
  179. call CUT()
  180. if same = 1 then do
  181.         call SETLAYER(fg)
  182. end
  183. call PASTE()
  184.  
  185. return
  186.  
  187.  
  188. setup:
  189.  
  190. /* Setup state variables, reading stored ones, if any. */
  191. num = 500
  192. lines = 1
  193. size = 1.05
  194. rot = 0
  195. rotcenter = center
  196. sizcenter= center
  197. segments = 2
  198. axis = 1
  199. jitter = ((x1-word(center,1))/10) ((y1-word(center,2))/10)
  200. jitter = jitter||((z1-word(center,3))/10)
  201. jittertype = 1
  202. same = 1
  203.  
  204. if (exists(statfil)) then do
  205.     if (~open(state, statfil, 'R')) then break
  206.     if (readln(state) ~= version) then break
  207.     num = readln(state)
  208.     lines = readln(state)
  209.     segments = readln(state)
  210.     size = readln(state)
  211.     sizcenter = readln(state)
  212.     rot = readln(state)
  213.     rotcenter = readln(state)
  214.     axis = readln(state)
  215.     jitter = readln(state)
  216.     jittertype = readln(state)
  217.     same = readln(state)
  218.     call close state
  219. end
  220.  
  221. /* Query user for their function and area to evaluate. */
  222. call req_begin syscode
  223.  
  224. id_num = req_addcontrol("Max. Number of Points", 'N')
  225. id_lines= req_addcontrol("Create lines", 'B')
  226. id_segments = req_addcontrol("Number of Segments", 'N')
  227. id_size= req_addcontrol("Line Size (Scaling)", 'N')
  228. id_sizcenter= req_addcontrol("Sizing Center","V",1)
  229. id_rot= req_addcontrol("Line Angle (Rotation)", 'N')
  230. id_rotcenter= req_addcontrol("Rotation Center", 'V', 1)
  231. id_axis= req_addcontrol("Rotation Axis", C, "X Y Z")
  232. id_jitter= req_addcontrol("Jitter Amount", V, 1)
  233. id_jittertype= req_addcontrol("Jitter Type", C, "Uniform Gaussian
  234.  Normal Radial")
  235. id_same= req_addcontrol("Put Results on Original Layer", 'B')
  236.  
  237. call req_setval id_num, num
  238. call req_setval id_lines, lines
  239. call req_setval id_segments, segments
  240. call req_setval id_size, size
  241. call req_setval id_sizcenter, sizcenter
  242. call req_setval id_rot, rot
  243. call req_setval id_rotcenter, rotcenter
  244. call req_setval id_axis, axis
  245. call req_setval id_jitter, jitter
  246. call req_setval id_jittertype, jittertype
  247. call req_setval id_same, same
  248.  
  249. if (~req_post()) then do
  250.     call req_end
  251.     return 0
  252. end
  253.  
  254. num = req_getval(id_num) % 1
  255. lines = req_getval(id_lines)
  256. segments = req_getval(id_segments)
  257. size = req_getval(id_size)
  258. sizcenter = req_getval(id_sizcenter)
  259. rot = req_getval(id_rot)
  260. rotcenter = req_getval(id_rotcenter)
  261. axis = req_getval(id_axis)
  262. jitter= req_getval(id_jitter)
  263. jittertype = req_getval(id_jittertype)
  264. same = req_getval(id_same)
  265.  
  266. call req_end
  267.  
  268. /* Save state now, in case something fails. */
  269. if (open(state, statfil, 'W')) then do
  270.     call writeln state, version
  271.     call writeln state, num
  272.     call writeln state, lines
  273.     call writeln state, segments
  274.     call writeln state, size
  275.     call writeln state, sizcenter
  276.     call writeln state, rot
  277.     call writeln state, rotcenter
  278.     call writeln state, axis
  279.     call writeln state, jitter
  280.     call writeln state, jittertype
  281.     call writeln state, same
  282.     call close state
  283. end
  284.  
  285. return 1
  286.  
  287. /* How does it work?  It copies all the points created by prick, sizes
  288.    them, rotates them, and jitters them.  Finally it draws a line
  289.    connecting each of the old points with the new, modified copies of
  290.    themselves.  If you use good values, the results are good hair. */
  291. hair:
  292.  
  293. if size >= 1 then do
  294.         sizepreset = (size - 1) / segments
  295. end
  296. else do
  297.         sizepreset = -((1 - size) / segments)
  298. end
  299.  
  300. size = 1 + sizepreset
  301.  
  302. if rot > 0 then do
  303.         rot = rot / segments
  304. end
  305.  
  306. do l = 1 to segments
  307.         call COPY()
  308.         call SCALE(size size size,sizcenter)
  309.         IF rot~=0 then do
  310.                 call ROTATE(rot,translate(axis,"XYZ","123"),rotcenter)
  311.         end
  312.         select
  313.         when jittertype=1 then jittertype="Gaussian"
  314.         when jittertype=2 then jittertype="Uniform"
  315.         when jittertype=3 then jittertype="Normal"
  316.         otherwise jittertype="Radial"
  317.         end
  318. /* Lightwave 3.0-3.1 users, type "call JITTER(jitter)" instead of
  319.    next line. */
  320.         call JITTER(jitter,jittertype)
  321.         call PASTE()
  322.  
  323.         n = xfrm_begin()
  324.         if n=0 then return
  325.  
  326. /* The meters can get a little corrupted, but it is okay */
  327.  
  328.         call meter_begin n, 'Drawing 'n/2' Lines', "Step 1"
  329.  
  330.         do i = 1 to n
  331.           Point.i= xfrm_getpos(i)
  332.           call meter_step
  333.         end
  334.         call xfrm_end()
  335.         call CUT()
  336.  
  337.         call meter_end()
  338.  
  339. /* Modeler works very strangely here.  It seems the COPY() and PASTE()
  340.    also copy the point order.  So that when you paste, let's say, point #2
  341.    and there is already point #2 in that layer, it makes the new point
  342.   point #3, and the old point number #3 becomes #4 (ie. it inserts
  343.    points in the middle of the points already in the layer).
  344.    If COPY() and PASTE() worked as it should be expected, so that if you
  345.    have 300 points in the layer, and copy other 200 to it, these new 200
  346.    points would go from 301-500, then this loop would be different.  It
  347.    would go from 1 to n/2 and the second point added would be i=i+n,
  348.    insted of i=i+1.  Weird!  It took me three hours to figure this out.
  349.    This is not mentioned in the Arexx docs. */
  350.  
  351.         call meter_begin n/2, 'Drawing 'n/2' Lines', "Step 2"
  352.         call add_begin()
  353.         s=0
  354.         do i=1 to n
  355.           s=s+1
  356.           call add_point Point.i
  357.           i=i+1
  358.           call add_point Point.i
  359.           s=s+1
  360.           call add_polygon s-1 s
  361.           call meter_step
  362.         end i
  363.         call add_end()
  364.         call meter_end()
  365.  
  366.         call CUT()
  367.         call setlayer(temp2)
  368.         call PASTE()
  369.         call MERGEPOINTS()
  370.  
  371.         call setlayer(temp)
  372.  
  373.         call add_begin()
  374.         do i=1 to n-1
  375.           i=i+1
  376.           call add_point Point.i
  377.         end
  378.         call add_end()
  379.  
  380. end l
  381.  
  382. return
  383.  
  384. /* End of Macro */
  385.